/*
    <one line to give the library's name and an idea of what it does.>
    Copyright (C) 2013  Guillaume <email>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/


#include "msVisualizerAtoms.h"


namespace atomism
{


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// msDynamicManager
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

bool msDynamicManager::isDynamicManagerRegisteredInPython=0;
msRegistrar msDynamicManager::Registrar("msDynamicManager", msDynamicManager::createInstance);

void msDynamicManager::registryInPython() {

  #if USE_PYTHON
  msTreeMapper::registryInPython();
  
  if( ! msDynamicManager::isDynamicManagerRegisteredInPython ) {
    
    using namespace boost::python;
    
     class_<msDynamicManager,bases<msTreeMapper>,boost::shared_ptr<msDynamicManager> >
       ("msDynamicManager",
	"Visualizer of atomic system's dynamic",no_init)
    .def( "New", &msDynamicManager::New , "Create a new object.") 
    .staticmethod("New")
    .def("watch",&msDynamicManager::watch,"watch the dynamic")
    .def("set_dt",&msDynamicManager::set_dt,"set the time step. Arg1: dt [ms]");;
	
    msDynamicManager::isDynamicManagerRegisteredInPython=1;
    }
#endif
 }  

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

void msDynamicManager::launch()
{
 renderer =  vtkSmartPointer<vtkRenderer>::New();
 vtkSmartPointer<vtkRenderWindow> renderWindow =  vtkSmartPointer<vtkRenderWindow>::New();
 renderWindow->AddRenderer(renderer);
  
 vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = 
               vtkSmartPointer<vtkRenderWindowInteractor>::New();
 renderWindowInteractor->SetRenderWindow(renderWindow);

 renderer->SetBackground(25./255.,50./255.,100/255.); // Background color white
 renderWindow->Render();
  // Initialize must be called prior to creating timer events.
 renderWindowInteractor->Initialize();

 vtkStyleInteract = vtkInteractorStyleTrackballCamera::New();
 renderWindowInteractor->AddObserver(vtkCommand::TimerEvent, this);
 renderWindowInteractor->CreateRepeatingTimer(dt);
 renderWindowInteractor->Start();
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

void msDynamicManager::Execute( vtkObject *caller, unsigned long eventId,
                                void * vtkNotUsed(callData) )
{
 if (vtkCommand::TimerEvent == eventId) {
     
     if( qi->Value > qi->Max) TimerCount=0; 
               
     vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::SafeDownCast(caller);
     if(vtkStyleInteract==0) iren->SetInteractorStyle(vtkStyleInteract);
			     
     KineticOperator->getSystem()->getPositions(Coors0);

     //if( TimerCount == 0 )  { qi->Value=qi->Min; KineticOperator->setPositions(); }
     if( TimerCount >= 0 )  KineticOperator->move(indice); 
     
     KineticOperator->getSystem()->getPositions(Coors1);
     msKineticWork work=KineticOperator->getSystem()->kineticWork(Coors0,Coors1);
     stringstream text; text<<"J="<<work.dJ.norm()<<" P="<<work.dP.norm()<<" Ek="<<work.dEkin<<endl;

     vtkSmartPointer<vtkRenderWindow> renderWindow =  iren->GetRenderWindow(); 
     vtkSmartPointer<vtkRenderer> renderer = renderWindow->GetRenderers()->GetFirstRenderer();
     clearRendered(renderer);
     renderer->SetBackground(.3, .6, .3);
     Visualizer->addToRenderer( KineticOperator->getSystem() , renderer );

     vtkSmartPointer<vtkTextActor> textActor = vtkSmartPointer<vtkTextActor>::New();
     textActor->GetTextProperty()->SetFontSize ( 16 );
     textActor->SetPosition2 ( 0, 10 );
     renderer->AddActor2D ( textActor );
     textActor->SetInput ( text.str().c_str() );
     textActor->GetTextProperty()->SetColor ( 1.0,1.0,1.0 );

     renderWindow->Render();


     TimerCount++;
     }

 }

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

void msDynamicManager::clearRendered(vtkRenderer* Renderer) {

 vtkActorCollection *Actors = Renderer->GetActors();
 vtkActor *Actor;
 for( Actors->InitTraversal(); (Actor = Actors->GetNextItem())!=NULL; )
     Renderer->RemoveActor( Actor );
    

 vtkActor2DCollection *Actors2D = Renderer->GetActors2D();
 vtkActor2D *Actor2D;
 for( Actors2D->InitTraversal(); (Actor2D = Actors2D->GetNextItem())!=NULL; )
     Renderer->RemoveActor2D( Actor2D );
 Renderer->Clear(); 
 }


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// msVisualizerAtoms
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------


bool msVisualizerAtoms::isVisualizerAtomsRegisteredInPython=0;
msRegistrar msVisualizerAtoms::Registrar("msVisualizerAtoms", msVisualizerAtoms::createInstance);


void msVisualizerAtoms::registryInPython() {

#if USE_PYTHON
  msVisualizerSystem::registryInPython();
  
  if( ! msVisualizerAtoms::isVisualizerAtomsRegisteredInPython ) {
    
    using namespace boost::python;
    
     class_<msVisualizerAtoms,bases<msVisualizerSystem>,boost::shared_ptr<msVisualizerAtoms> >
       ("msVisualizerAtoms",
	"Visualizer of atomic system",no_init)
    .def( "New", &msVisualizerAtoms::New , "Create a new object.") 
    .staticmethod("New")
    .def("setResolution",&msVisualizerAtoms::setResolution,"set the resolution");
	
    msVisualizerAtoms::isVisualizerAtomsRegisteredInPython=1;
    }
#endif
 }  

void msVisualizerAtoms::setResolution(int r) {
  
 Resolution=r; 
 }

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

void msVisualizerAtoms::watch(boost::shared_ptr<msSystem> system){
 
 Objects=ms3dStructure::New();
 createAtoms(system);
 createBonds(system);
 Objects->show();
 }
 
void msVisualizerAtoms::addToRenderer( boost::shared_ptr<msSystem> system , vtkRenderer* Renderer ) {

 Objects=ms3dStructure::New();
 createAtoms(system);
 createBonds(system);
 Objects->show(Renderer);
 }

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

void msVisualizerAtoms::createAtoms(boost::shared_ptr<msSystem> system) {
 
 msSystem::ElementItr it=system->beginElements();
 Objects->clear();
 msAtom* atom;
 for(int i=0;it!=system->endElements();it++,++i) {
    
    Eigen::Vector3d pos=it.dereference().getPosition();
    if( (*it).isDerivedFrom("msAtom") ) atom = (msAtom*)( &(it.dereference()) );
    boost::shared_ptr<ms3dSphere> sphere = ms3dSphere::New();
    sphere->setGeometry(pos[0],pos[1],pos[2],atom->getVdWRadius(system->getUnits()->Length)/2. );
    sphere->setResolution(Resolution);
    sphere->setColor(atom->Color);
    Objects->add( boost::static_pointer_cast<ms3dObject>(sphere) );
    };
}
  
void msVisualizerAtoms::createBonds(boost::shared_ptr<msSystem> system){
  
 int order; 
 msAtom* atom1;msAtom* atom2;
 msSystem::ElementItr it1=system->beginElements();
 msSystem::ElementItr it2=system->beginElements();

 for( ; it1 != system->endElements(); it1++ )
    if( (*it1).isDerivedFrom("msAtom") ) {
      
       atom1 =(msAtom*)( &(it1.dereference()) );
       
       for( it2=it1; it2 != system->endElements() ; it2++) 
           if( (*it2).isDerivedFrom("msAtom")  ) {
	        
	        atom2 = (msAtom*)( &(it2.dereference()) );
	
	        if( (order=isBondedByCovalent(atom1,atom2))>0 ) {

	           boost::shared_ptr<ms3dTube> bond1= ms3dTube::New();
	           boost::shared_ptr<ms3dTube> bond2= ms3dTube::New();
		   Eigen::Vector3d v0 = atom1->getPosition();
	           Eigen::Vector3d v1 = v0+( atom2->getPosition()-atom1->getPosition())/2;
		   Eigen::Vector3d v2 = atom2->getPosition();
	           bond1->setGeometry(v0,v1, 0.05*order);
	           bond1->setColor(atom1->Color);
		   bond1->setResolution(Resolution);
	           Objects->add(bond1);
	           bond2->setGeometry(v1,v2,0.05*order);
	           bond2->setColor(atom2->Color);
		   bond2->setResolution(Resolution);
	           Objects->add(bond2); 
	           }
	        }
         }      
    
 }
 
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

int msVisualizerAtoms::isBondedByCovalent(msAtom* a1,msAtom* a2) {

 if(a1==a2) return 0;
 Eigen::Vector3d v = a1->getPosition()-a2->getPosition();
 int order=0;
 for( int k=0; k<3 ; k++) {

     double covlength = 1.1 * getCovalentLength(k,*a1,*a2);
     if( v.norm()<covlength) order=k;
     }
 return order;
 }
}
